#include <tinx/interrupt.h>
#include <tinx/io.h>

#define PIC_M_CTRL 0x20
#define PIC_M_DATA 0x21
#define PIC_S_CTRL 0xA0
#define PIC_S_DATA 0xA1

#define EOI 0x20

#define ICW1_ICW4       0x01        /* Indicates that ICW4 will be present */
#define ICW1_SINGLE     0x02        /* Single (cascade) mode */
#define ICW1_INTERVAL4  0x04        /* Call address interval 4 (8) */
#define ICW1_LEVEL      0x08        /* Level triggered (edge) mode */
#define ICW1_INIT       0x10        /* Initialization - required! */

#define ICW4_8086       0x01        /* 8086/88 (MCS-80/85) mode */
#define ICW4_AUTO       0x02        /* Auto (normal) EOI */
#define ICW4_BUF_SLAVE  0x08        /* Buffered mode/slave */
#define ICW4_BUF_MASTER 0x0C        /* Buffered mode/master */
#define ICW4_SFNM       0x10        /* Special fully nested (not) */

void pic_disable()
{
    outb(PIC_M_DATA, 0xff);
    outb(PIC_S_DATA, 0xff);
}

void pic_send_eoi(u8 irq)
{
    outb(PIC_M_CTRL, EOI);
    if (irq >= 8)
        outb(PIC_S_CTRL, EOI);
}

void pic_set_mask(u8 irq, bool value)
{
    u16 port = PIC_M_DATA;
    if (irq >= 8)
    {
        irq -= 8;
        port = PIC_S_DATA;

        // 打开 IRQ2 连接 PIC 从片
        set_interrupt_mask(0x2, true);
    }
    outb(port, inb(port) & ~(1 << irq));
}

ic_op_t pic_op = {
    pic_set_mask,
    pic_send_eoi,
};

void pic_init()
{
    outb(PIC_M_CTRL, ICW1_INIT | ICW1_ICW4);
    outb(PIC_S_CTRL, ICW1_INIT | ICW1_ICW4);
    outb(PIC_M_DATA, 0x20);
    outb(PIC_S_DATA, 0x28);

    outb(PIC_M_DATA, 0b00000100);
    outb(PIC_S_DATA, 2);
    outb(PIC_M_DATA, ICW4_8086);
    outb(PIC_S_DATA, ICW4_8086);

    outb(PIC_M_DATA, 0xff);
    outb(PIC_S_DATA, 0xff);

    ic_set_op(&pic_op);
}